﻿using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using UIOC.CodeAnalysis.Extensions;
using UIOC.CodeAnalysis.Helpers;

namespace UIOC.CodeAnalysis.Generators;

internal static class TypeRegistrationGenerator {

    public readonly struct GeneratedInfo(GeneratedSource source) {

        public GeneratedSource GeneratedSource { get; } = source;
    }

    public static string VAR_NAME_TYPE_MAP = "typeMap";
    public static string CLASS_NAME_TYPE_REGISTRATIONS = "TypeRegistration";

    private static void LogInfo(string message) {
        SourceOutputHelper.LogInfo(DiagnosticSeverity.Info, $"[{nameof(TypeRegistrationGenerator)}] {message}");
    }

    public static ImmutableArray<GeneratedInfo> GenerateTypeRegistration(
        Compilation compilation,
        ImmutableDictionary<INamedTypeSymbol, BeanGenerator.GeneratedInfo> beanGeneratedSourceDictionary
    ) {
        var implementAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.Implement);
        if (implementAttributeSymbol == null) {
            return ImmutableArray<GeneratedInfo>.Empty;
        }

        var instanceTypeAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.InstanceType);
        if (instanceTypeAttributeSymbol == null) {
            return ImmutableArray<GeneratedInfo>.Empty;
        }

        var instanceTypeEnumSymbol = compilation.GetTypeByMetadataName(TypeFullNames.InstanceType);
        if (instanceTypeEnumSymbol == null) {
            return ImmutableArray<GeneratedInfo>.Empty;
        }

        var typeMapTypeSymbol = compilation.GetTypeByMetadataName(TypeFullNames.ITypeMapInternal);
        if (typeMapTypeSymbol == null) {
            return ImmutableArray<GeneratedInfo>.Empty;
        }

        // 创建全局命名空间引用
        var uiocNamespaceSyntax = SyntaxFactory.IdentifierName($"global::{NamespaceNames.UIOC}");
        var internalNamespaceSyntax = SyntaxFactory.IdentifierName($"global::{NamespaceNames.Internal}");
        var internalUIOCNamespaceSyntax = SyntaxFactory.QualifiedName(internalNamespaceSyntax, SyntaxFactory.IdentifierName(NamespaceNames.UIOC));

        // 创建 global::UIOC.Application.TypeMap 表达式
        var applicationExpressionSyntax = SyntaxFactory.MemberAccessExpression(
            SyntaxKind.SimpleMemberAccessExpression,
            uiocNamespaceSyntax,
            SyntaxFactory.IdentifierName(TypeNames.Application)
        );

        var typeMapExpressionSyntax = SyntaxFactory.MemberAccessExpression(
            SyntaxKind.SimpleMemberAccessExpression,
            applicationExpressionSyntax,
            SyntaxFactory.IdentifierName(TypeNames.TypeMap)
        );

        var typeMapInternalExpressionSyntax = SyntaxFactory.MemberAccessExpression(
            SyntaxKind.SimpleMemberAccessExpression,
            internalUIOCNamespaceSyntax,
            SyntaxFactory.IdentifierName(TypeNames.ITypeMapInternal)
        );

        // 创建 as global::Internal.UIOC.ITypeMapInternal 表达式
        var expressionSyntax = SyntaxFactory.BinaryExpression(
            SyntaxKind.AsExpression,
            typeMapExpressionSyntax,
            typeMapInternalExpressionSyntax
        );

        // 构建变量声明: var typeMap = ...
        var variableDeclarationSyntax = SyntaxFactory.VariableDeclaration(
            SyntaxFactory.IdentifierName("var")
        ).AddVariables(SyntaxFactory.VariableDeclarator(
            SyntaxFactory.Identifier(VAR_NAME_TYPE_MAP)
        ).WithInitializer(SyntaxFactory.EqualsValueClause(expressionSyntax)));

        var typeMapStatementSyntax = SyntaxFactory.LocalDeclarationStatement(variableDeclarationSyntax)
            .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));

        LogInfo($"[Assembly({compilation.Assembly.Name})] {nameof(GenerateTypeRegistration)} ...");

        var result = ImmutableArray.CreateBuilder<GeneratedInfo>();

        var objectSymbols = beanGeneratedSourceDictionary.Keys;

        // 为每个需要注册的类型创建 RegisterMethod调用
        var registrationStatementSyntaxList = new List<StatementSyntax>();
        var generatedHashSet = new HashSet<string>();

        foreach (var objectSymbol in objectSymbols) {
            GenerateRegistrationStatement(
                objectSymbol,
                implementAttributeSymbol,
                instanceTypeAttributeSymbol,
                instanceTypeEnumSymbol,
                registrationStatementSyntaxList,
                generatedHashSet
            );
        }

        // 如果有需要生成的注册代码
        if (registrationStatementSyntaxList.Any()) {

            // 插入 var type
            registrationStatementSyntaxList.Insert(0, typeMapStatementSyntax);

            // 构建UnityEngine.RuntimeInitializeLoadType枚举的引用
            var runtimeInitializeLoadTypeSyntax = SyntaxFactory.QualifiedName(
                SyntaxFactory.IdentifierName("global::UnityEngine"),
                SyntaxFactory.IdentifierName("RuntimeInitializeLoadType")
            );

            // 构建AfterAssembliesLoaded枚举值
            var afterAssembliesLoadedSyntax = SyntaxFactory.QualifiedName(
                runtimeInitializeLoadTypeSyntax,
                SyntaxFactory.IdentifierName("AfterAssembliesLoaded")
            );

            var attributeArgumentSyntax = SyntaxFactory.AttributeArgument(afterAssembliesLoadedSyntax);
            var runtimeAttributeSyntax = SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(
                SyntaxFactory.IdentifierName("global::UnityEngine"),
                SyntaxFactory.IdentifierName("RuntimeInitializeOnLoadMethod")
            )).AddArgumentListArguments(attributeArgumentSyntax);

            var ifDirectiveSyntax = SyntaxFactory.IfDirectiveTrivia(
                SyntaxFactory.IdentifierName("UNITY_5_3_OR_NEWER"),
                isActive: true,
                branchTaken: true,
                conditionValue: true
            );
            var endIfDirectiveSyntax = SyntaxFactory.EndIfDirectiveTrivia(isActive: true);
            var runtimeAttributeListSyntax = SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([
                runtimeAttributeSyntax
            ])).WithLeadingTrivia(
                SyntaxFactory.Trivia(ifDirectiveSyntax),
                SyntaxFactory.CarriageReturnLineFeed
            ).WithTrailingTrivia(
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.Trivia(endIfDirectiveSyntax)
            );

            // 创建注册函数
            var registerTypesMemberDeclarationSyntax = SyntaxFactory.MethodDeclaration(
                SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)),
                "RegisterTypes"
            ).AddModifiers(
                SyntaxFactory.Token(SyntaxKind.PrivateKeyword),
                SyntaxFactory.Token(SyntaxKind.StaticKeyword)
            ).WithBody(
                SyntaxFactory.Block(registrationStatementSyntaxList)
            ).AddAttributeLists(runtimeAttributeListSyntax);

            var registrationClassDeclarationSyntax = SyntaxFactory.ClassDeclaration(CLASS_NAME_TYPE_REGISTRATIONS)
                .AddModifiers(
                    SyntaxFactory.Token(SyntaxKind.InternalKeyword),
                    SyntaxFactory.Token(SyntaxKind.StaticKeyword),
                    SyntaxFactory.Token(SyntaxKind.PartialKeyword)
                )
                .AddMembers(registerTypesMemberDeclarationSyntax)
                .AddAttributeLists([
                    SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([
                        SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(
                            SyntaxFactory.IdentifierName($"global::{nameof(System)}.{nameof(System.Diagnostics)}"),
                            SyntaxFactory.IdentifierName(nameof(System.Diagnostics.DebuggerStepThroughAttribute))
                        )),
                    ])),
                    SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([
                        SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(
                            SyntaxFactory.IdentifierName($"global::{nameof(System)}.{nameof(System.Diagnostics)}"),
                            SyntaxFactory.IdentifierName(nameof(System.Diagnostics.DebuggerNonUserCodeAttribute))
                        )),
                    ]))
                ]);

            var leadingTriviaSyntax = SyntaxFactory.Comment(UIOCGenerator.Leading);
            var namespaceDeclarationSyntax = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.IdentifierName(compilation.Assembly.Name))
                .WithLeadingTrivia(leadingTriviaSyntax)
                .AddMembers(registrationClassDeclarationSyntax);

            var compilationUnitSyntax = SyntaxFactory.CompilationUnit()
                .AddMembers(namespaceDeclarationSyntax);
            if (compilationUnitSyntax == null) {
                return ImmutableArray<GeneratedInfo>.Empty;
            }

            var outputSource = SourceText.From(compilationUnitSyntax.NormalizeWhitespace().ToFullString(), Encoding.UTF8);
            result.Add(new GeneratedInfo(
                new GeneratedSource(
                    hintName: CLASS_NAME_TYPE_REGISTRATIONS,
                    assemblyName: compilation.Assembly.Name,
                    namespaceName: compilation.Assembly.Name,
                    sourceText: outputSource,
                    compilationUnitSyntax: compilationUnitSyntax,
                    generatorName: nameof(TypeRegistrationGenerator)
                )
            ));
        }
        return result.ToImmutable();
    }

    private static void GenerateRegistrationStatement(
        INamedTypeSymbol classSymbol,
        INamedTypeSymbol implementAttributeSymbol,
        INamedTypeSymbol instanceTypeAttributeSymbol,
        INamedTypeSymbol instanceTypeEnumSymbol,
        List<StatementSyntax> registrationStatementSyntaxList,
        HashSet<string> generatedHashSet
    ) {
        var instanceTypeAttributeData = classSymbol.AllInterfaces.SelectMany(@interface => @interface.GetAllAttributes(instanceTypeAttributeSymbol)).FirstOrDefault();
        var implementAttributeDatas = classSymbol.GetAttributes(implementAttributeSymbol);
        foreach (var implementAttributeData in implementAttributeDatas) {
            if (implementAttributeData.ConstructorArguments.Length > 0
                && implementAttributeData.ConstructorArguments[0].Value is INamedTypeSymbol interfaceSymbol
                && interfaceSymbol.TypeKind == TypeKind.Interface
            ) {
                var label = $"global::{classSymbol.ContainingNamespace.GetFullNamespaceName()}_{classSymbol.Name}+global::{interfaceSymbol.ContainingNamespace.GetFullNamespaceName()}_{interfaceSymbol.Name}";
                if (generatedHashSet.Contains(label)) {
                    continue;
                }

                // 创建 typeof(InterfaceType) 参数
                var interfaceTypeSyntax = CreateTypeOfExpression(interfaceSymbol);

                // 创建 typeof(ImplementedType) 参数
                var implementedTypeSyntax = CreateTypeOfExpression(classSymbol);

                var instanceTypeEnumValue = instanceTypeAttributeData?.ConstructorArguments.Length > 0
                    ? (int)instanceTypeAttributeData.ConstructorArguments[0].Value
                    : 0;

                // 创建 RegisterMethod 调用
                var registerMethodInvocationExpressionSyntax = SyntaxFactory.InvocationExpression(
                    SyntaxFactory.MemberAccessExpression(
                        SyntaxKind.SimpleMemberAccessExpression,
                        SyntaxFactory.IdentifierName(VAR_NAME_TYPE_MAP),
                        SyntaxFactory.IdentifierName(MemberNames.ITypeMapInternal.Register)
                    )
                ).AddArgumentListArguments(
                    SyntaxFactory.Argument(interfaceTypeSyntax),
                    SyntaxFactory.Argument(implementedTypeSyntax),
                    SyntaxFactory.Argument(SyntaxFactory.CastExpression(
                        instanceTypeEnumSymbol.CreateSimpleNameSyntax(),
                        SyntaxFactory.LiteralExpression(
                            SyntaxKind.NumericLiteralExpression,
                            SyntaxFactory.Literal(instanceTypeEnumValue)
                        )
                    )),
                    SyntaxFactory.Argument(
                        SyntaxFactory.NameColon("logEnabled"),
                        SyntaxFactory.Token(SyntaxKind.None),
                        SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression)
                    )
                );

                registrationStatementSyntaxList.Add(SyntaxFactory.ExpressionStatement(registerMethodInvocationExpressionSyntax));
                generatedHashSet.Add(label);
            }
        }
    }

    private static TypeOfExpressionSyntax CreateTypeOfExpression(INamedTypeSymbol typeSymbol) {
        var noNamespace = typeSymbol.TypeKind == TypeKind.Interface
            ? string.IsNullOrEmpty(typeSymbol.ContainingNamespace.Name)
            : string.IsNullOrEmpty(typeSymbol.ContainingAssembly.Name);
        var rootNamespaceSyntax = noNamespace ?
            null :
            SyntaxFactory.IdentifierName($"global::{(typeSymbol.TypeKind == TypeKind.Interface
                ? typeSymbol.ContainingNamespace.GetFullNamespaceName()
                : $"{typeSymbol.ContainingAssembly.Name}.{CLASS_NAME_TYPE_REGISTRATIONS}"
            )}");
        var typeName = typeSymbol.TypeKind == TypeKind.Interface ? typeSymbol.Name : $"{typeSymbol.Name}{BeanGenerator.BEAN_TYPE_SUFFIX}";
        if (typeSymbol.IsGenericType) {
            // 处理泛型类型
            var genericNameSyntax = typeSymbol.IsUnboundGenericType
                ? SyntaxFactory.GenericName(
                    SyntaxFactory.Identifier(typeName)
                ).AddTypeArgumentListArguments([.. typeSymbol.TypeArguments.Select(arg =>
                    SyntaxFactory.IdentifierName(string.Empty)
                )])
                : SyntaxFactory.GenericName(
                    SyntaxFactory.Identifier(typeName)
                ).AddTypeArgumentListArguments([.. typeSymbol.TypeArguments.Select(arg =>
                    arg is INamedTypeSymbol namedTypeSymbol
                        ? namedTypeSymbol.CreateSimpleNameSyntax()
                        : SyntaxFactory.IdentifierName(string.Empty)
                )]);
            NameSyntax nameSyntax = noNamespace
                    ? SyntaxFactory.IdentifierName($"global::{genericNameSyntax}")
                    : SyntaxFactory.QualifiedName(
                        rootNamespaceSyntax,
                        genericNameSyntax
                    );
            return SyntaxFactory.TypeOfExpression(nameSyntax);
        } else {
            // 处理非泛型类型
            NameSyntax nameSyntax = noNamespace
                ? SyntaxFactory.IdentifierName($"global::{typeName}")
                : SyntaxFactory.QualifiedName(
                    rootNamespaceSyntax,
                    SyntaxFactory.IdentifierName(typeName)
                );
            return SyntaxFactory.TypeOfExpression(nameSyntax);
        }
    }
}